package br.eti.busarello;

import java.awt.Component;
import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

/*
 *	Class to manage the widths of colunmns in a table.
 *
 *  Various properties control how the width of the column is calculated.
 *  Another property controls whether column width calculation should be dynamic.
 *  Finally, various Actions will be added to the table to allow the user
 *  to customize the functionality.
 *
 *  This class was designed to be used with tables that use an auto resize mode
 *  of AUTO_RESIZE_OFF. With all other modes you are constrained as the width
 *  of the columns must fit inside the table. So if you increase one column, one
 *  or more of the other columns must decrease. Because of this the resize mode
 *  of RESIZE_ALL_COLUMNS will work the best.
 */
public class TableColumnAdjuster implements PropertyChangeListener, TableModelListener {
	private JTable table;
	private int spacing;

	/*
	 * Specify the table and use default spacing
	 */
	public TableColumnAdjuster(JTable table) {
		this(table, 6);
	}

	/*
	 * Specify the table and spacing
	 */
	public TableColumnAdjuster(JTable table, int spacing) {
		this.table = table;
		this.spacing = spacing;

		table.addPropertyChangeListener(this);
		table.getModel().addTableModelListener(this);

	}

	/*
	 * Adjust the widths of all the columns in the table
	 */
	public void adjustColumns() {
		TableColumnModel tcm = table.getColumnModel();

		for (int i = 0; i < tcm.getColumnCount(); i++) {
			adjustColumn(i);
		}
	}

	/*
	 * Adjust the width of the specified column in the table
	 */
	public void adjustColumn(final int column) {
		TableColumn tableColumn = table.getColumnModel().getColumn(column);

		if (!tableColumn.getResizable())
			return;

		int columnHeaderWidth = getColumnHeaderWidth(column);
		int columnDataWidth = getColumnDataWidth(column);
		int preferredWidth = Math.max(columnHeaderWidth, columnDataWidth);

		updateTableColumn(column, preferredWidth);
	}

	/*
	 * Calculated the width based on the column name
	 */
	private int getColumnHeaderWidth(int column) {

		TableColumn tableColumn = table.getColumnModel().getColumn(column);
		Object value = tableColumn.getHeaderValue();
		TableCellRenderer headerRenderer = tableColumn.getHeaderRenderer();
		
		Component component;
		
		if(headerRenderer != null){
			component = headerRenderer.getTableCellRendererComponent(table, value, false, false, -1, column);
		}else{
			component = table.getTableHeader().getDefaultRenderer().getTableCellRendererComponent(table, value, false, false, -1, column);
		}
		Dimension preferredSize = component.getPreferredSize();

		return preferredSize.width;
	}

	/*
	 * Calculate the width based on the widest cell renderer for the given column.
	 */
	private int getColumnDataWidth(int column) {

		int preferredWidth = 0;

		int rowCount = table.getRowCount();

//		Font font = table.getFont();
//		FontMetrics fontMetrics = table.getFontMetrics(font);

		for (int i = 0; i < rowCount; i++) {

//			Object value = table.getValueAt(i, column);
//			if (value != null) {
//				int dataPreferredWidth = fontMetrics.stringWidth(value.toString());
//
//				if (dataPreferredWidth > preferredWidth) {
//					preferredWidth = dataPreferredWidth;
//				}
//			}
			
			Object value = table.getValueAt(i, column);
			
			TableCellRenderer cellRenderer = table.getCellRenderer(rowCount, column);
			
			Component component = cellRenderer.getTableCellRendererComponent(table, value, false, false, i, column);
			
			Dimension preferredSize = component.getPreferredSize();
			
			if(preferredSize.width > preferredWidth){
				preferredWidth = preferredSize.width;
			}
			
			
		}

		return preferredWidth;
	}

	/*
	 * Update the TableColumn with the newly calculated width
	 */
	private void updateTableColumn(int column, int width) {
		final TableColumn tableColumn = table.getColumnModel().getColumn(column);

		width += spacing;

		tableColumn.setPreferredWidth(width);
	}

	//
	// Implement the PropertyChangeListener
	//
	@Override
	public void propertyChange(PropertyChangeEvent e) {
		// When the TableModel changes we need to update the listeners
		// and column widths

		if ("model".equals(e.getPropertyName())) {
			TableModel model = (TableModel) e.getOldValue();
			model.removeTableModelListener(this);

			model = (TableModel) e.getNewValue();
			model.addTableModelListener(this);
			adjustColumns();
		}
	}

	//
	// Implement the TableModelListener
	//
	@Override
	public void tableChanged(TableModelEvent e) {
		// A cell has been updated

		if (e.getType() == TableModelEvent.UPDATE) {
			int column = table.convertColumnIndexToView(e.getColumn());

			// Only need to worry about an increase in width for this cell

			adjustColumn(column);
		}

		// The update affected more than one column so adjust all columns

		else {
			adjustColumns();
		}
	}
}